#include "stdafx.h"
#include "cppnode.h"
#include "AstBuilder.h"

using namespace CppClean;

#define _NAMESPACE_POP		"ns-pop"

//////////////////////////////////////////////////////////////////////////
//CAstBuilder
//////////////////////////////////////////////////////////////////////////
CAstBuilder::CAstBuilder(CTokenizer* pTokenizer, 
						 string sInClassName, 
						 int eVisibility /* = VISIBILITY_NONE */, 
						 const STRINGLIST& np /* = STRINGLIST */)
:m_pTokenizer(pTokenizer), m_eVisibility(eVisibility),m_aNameSpaces(np.begin(), np.end()), 
m_bHandlingTypeDef(FALSE), m_bInFunction(FALSE),m_sInClassName(sInClassName)
{
	if(!sInClassName.empty())
	{
		m_sCurrentClassName = sInClassName.substr(sInClassName.find_last_of("::") + 2);
	}
	m_pConverter = new CTypeConverter(m_aNameSpaces);
}

CAstBuilder::~CAstBuilder(void)
{
}

CCpAutoPtr<CCpObject> CAstBuilder::GenerateOne()
{
	if(m_pTokenizer == NULL)
		return NULL;

	while(1)
	{
		CCpAutoPtr<CToken> pToken = GetNextToken();
		if(pToken == NULL)
			break;

		m_pCurrentToken = pToken;
		if(pToken->m_eType == CToken::eTTInternal)
		{
			if(pToken->m_sName == _NAMESPACE_POP)
				m_aNameSpaces.pop_back();
			continue;
		}
		CCpAutoPtr<CCpObject> result = _GenerateOne(pToken);
		if(result != NULL)
			return result;
	}
	return NULL;
}

CCpAutoPtr<CToken> CAstBuilder::GetNextToken()
{
	CCpAutoPtr<CToken> pRet = NULL;
	if(!m_aTokenQues.empty())
	{
		pRet = m_aTokenQues.back();
		m_aTokenQues.pop_back();
	}
	else if(m_pTokenizer)
	{
		pRet = m_pTokenizer->GetNextToken();
	}
	return pRet;
}

void CAstBuilder::AddBackToken(CCpAutoPtr<CToken> pToken)
{
	if(pToken->m_eWhence == CToken::eWhenceStream)
	{
		pToken->m_eWhence = CToken::eWhenceQueue;
		m_aTokenQues.push_front(pToken);
	}
	else
	{
		assert(false);
		m_aTokenQues.push_back(pToken);
	}
}

void CAstBuilder::AddBackTokens(CToken::TOKEN_LIST aTokens)
{
	if(aTokens.empty())
		return;

	if(aTokens[aTokens.size() - 1]->m_eWhence == CToken::eWhenceStream)
	{
		for(int i = aTokens.size() - 1; i >= 0; i--)
		{
			CCpAutoPtr<CToken>& pToken = aTokens[i];
			pToken->m_eWhence = CToken::eWhenceQueue;
			m_aTokenQues.push_front(pToken);
		}
	}
	else
	{
		for(int i = aTokens.size() - 1; i >= 0; i--)
		{
			CCpAutoPtr<CToken>& pToken = aTokens[i];
			m_aTokenQues.push_back(pToken);
		}
	}
}

CTokenPtr CAstBuilder::GetVarTokensUpTo(int eExpectedType, LPCSTR szExpectedTokens, CTokenList& aTokens)
{
	STRINGSET sExpect;
	CStringUtil::Split(sExpect, szExpectedTokens);

	CTokenPtr pLastToken = GetNextToken();
	while(pLastToken != NULL && (pLastToken->m_eType != eExpectedType || 
		!CStringUtil::InSet(pLastToken->m_sName.c_str(), sExpect)))
	{
		aTokens.push_back(pLastToken);
		pLastToken = GetNextToken();
	}
	return pLastToken;
}

CObjectPtr CAstBuilder::_GenerateOne(CTokenPtr pToken)
{
	if(pToken == NULL)
		return NULL;
	if(pToken->m_eType == CToken::eTTName)
	{
		if(CKeyword::IsKeyword(pToken->m_sName.c_str()) && !CKeyword::IsBuiltInType(pToken->m_sName.c_str()))
		{
			return HandleKeyword(pToken->m_sName);
		}
		else if(pToken->m_sName == m_sCurrentClassName)
		{
			// The token name is the same as the class, must be a ctor if
			// there is a paren.  Otherwise, it's the return type.
			// Peek ahead to get the next token to figure out which.
			CTokenPtr pNext = GetNextToken();
			AddBackToken(pNext);
			if(pNext->m_eType == CToken::eTTSyntax && pNext->m_sName == "(")
			{
				//TODO
				return NULL;//GetMethod();
			}
		}
		// Handle data or function declaration/definition.
		CTokenList aTempTokens;
		CTokenPtr pLast = GetVarTokensUpTo(CToken::eTTSyntax, "( ; { [", aTempTokens);
		aTempTokens.push_front(pToken);
		if(pLast->m_sName == "(")
		{
			//If there is an assignment before the paren,
			//this is an expression, not a method.
			for(int i = 0; i < aTempTokens.size(); i++)
			{
				if(aTempTokens[i]->m_sName == "=")
				{
					CTokenList aNewTemp;
					CTokenPtr pLast1 = GetVarTokensUpTo(CToken::eTTSyntax, ";", aNewTemp);
					aTempTokens.push_back(pLast);
					aTempTokens.insert(aTempTokens.end(), aNewTemp.begin(), aNewTemp.end());
					assert(pLast1->m_sName == ";");
					aTempTokens.push_back(pLast1);
					pLast = pLast1;
					break;
				}
			}
		}

		if(pLast->m_sName == "[")
		{
			// Handle array, this isn't a method, unless it's an operator.
			// TODO: keep the size somewhere.
			aTempTokens.push_back(pLast);
			const CTokenPtr& pPrev2 = aTempTokens[aTempTokens.size() - 2];
			if(pPrev2 && pPrev2->m_sName == "operator")
			{
				aTempTokens.push_back(GetNextToken());
			}
			else
			{
				CTokenList aNewTemp;
				pLast = GetVarTokensUpTo(CToken::eTTSyntax, ";", aNewTemp);
				aTempTokens.insert(aTempTokens.end(), aNewTemp.begin(), aNewTemp.end());
			}
		}

		if(pLast->m_sName == ";")
		{
			//Handle data, this isn't a method.
			CType::TYPE_LIST templTypes;
			STRINGLIST templSubs, aModifiers;
			string sName, sTypeName;
			CToken::TOKEN_LIST aExtraDefaults, aUnUsedTokens;

//			m_pConverter->DeclarationToParts(aTempTokens, TRUE, )
		}
	}

	return NULL;
}

CObjectPtr CAstBuilder::HandleKeyword(const string& name)
{
	if(name == "class")
	{
		CCpAutoPtr<CClass> pClass = HandleClass();
		return (CCpObject*)pClass.Get();
	}
	else if(name == "struct")
	{
		CCpAutoPtr<CClass> pClass = HandleClass();
		return (CCpObject*)pClass.Get();
	}
	else
	{
	}
	return NULL;
}

CCpAutoPtr<CClass> CAstBuilder::HandleClass()
{
	return NULL;
}
